SetFlagsRule: check whether flag is finalized If a flag is finalized, then the SetFlagsRule cannot change the value of the flag. Thus in this case if the test requests to set the flag value as false, then this test should be skipped, since the flag is finalized on the current device. Test: atest FlagJUnitTests FlagJUnitHostTests Bug: 402588368 Change-Id: I7aceb849343e8169ce92b88894b2d5793d6189fe 
diff --git a/libraries/flag-helpers/junit/src_base/android/platform/test/flag/junit/SetFlagsRule.java b/libraries/flag-helpers/junit/src_base/android/platform/test/flag/junit/SetFlagsRule.java index 38fcbb5..d652c13 100644 --- a/libraries/flag-helpers/junit/src_base/android/platform/test/flag/junit/SetFlagsRule.java +++ b/libraries/flag-helpers/junit/src_base/android/platform/test/flag/junit/SetFlagsRule.java 
@@ -52,6 +52,7 @@  private static final String FLAG_CONSTANT_PREFIX = "FLAG_";  private static final String SET_FLAG_METHOD_NAME = "setFlag";  private static final String RESET_ALL_METHOD_NAME = "resetAll"; + private static final String IS_FLAG_FINALIZED_METHOD_NAME = "isFlagFinalized";  private static final String IS_FLAG_READ_ONLY_OPTIMIZED_METHOD_NAME = "isFlagReadOnlyOptimized";    // Store instances for entire life of a SetFlagsRule instance @@ -147,7 +148,6 @@  *  * @param fullFlagNames The name of the flags in the flag class with the format  * {packageName}.{flagName} - *  * @deprecated Annotate your test or class with <code>@EnableFlags(String...)</code> instead  */  @Deprecated @@ -168,7 +168,6 @@  *  * @param fullFlagNames The name of the flags in the flag class with the format  * {packageName}.{flagName} - *  * @deprecated Annotate your test or class with <code>@DisableFlags(String...)</code> instead  */  @Deprecated @@ -337,7 +336,8 @@  // TODO(b/337449119): SetFlagsRule should still run tests that are consistent with the  // read-only values of flags. But be careful, if a ClassRule exists, the value returned by  // the original FeatureFlags instance may be overridden, and reading it may not be allowed. - boolean isOptimized = verifyFlagReadOnlyAndOptimized(fakeFlagsImplInstance, flag); + boolean isOptimized = + verifyFlag(fakeFlagsImplInstance, flag, IS_FLAG_READ_ONLY_OPTIMIZED_METHOD_NAME);  assumeFalse(  String.format(  "Flag %s is read_only, and the code is optimized. " @@ -346,6 +346,16 @@  flag.fullFlagName()),  isOptimized);   + boolean isFinalized = + verifyFlag(fakeFlagsImplInstance, flag, IS_FLAG_FINALIZED_METHOD_NAME); + assumeFalse( + String.format( + "Flag %s is finalized on this device. " + + " The flag value should not be turned off on this device" + + " Skip this test.", + flag.fullFlagName()), + isFinalized && !value); +  // Set desired flag value in the FakeFeatureFlagsImpl  setFlagValueInFakeFeatureFlagsImpl(fakeFlagsImplInstance, flag, value);  } @@ -443,15 +453,14 @@  }  }   - private static boolean verifyFlagReadOnlyAndOptimized(Object fakeFeatureFlagsImpl, Flag flag) { + private static boolean verifyFlag(Object fakeFeatureFlagsImpl, Flag flag, String methodName) {  String fullFlagName = flag.fullFlagName();  try {  boolean result =  (Boolean)  fakeFeatureFlagsImpl  .getClass() - .getMethod( - IS_FLAG_READ_ONLY_OPTIMIZED_METHOD_NAME, String.class) + .getMethod(methodName, String.class)  .invoke(fakeFeatureFlagsImpl, fullFlagName);  return result;  } catch (NoSuchMethodException e) { @@ -466,9 +475,9 @@  throw new FlagSetException(  fullFlagName,  String.format( - "Cannot check whether flag is optimized. " + "Cannot invoke %s. "  + "Flag implementation %s is not fake implementation", - fakeFeatureFlagsImpl.getClass().getName()), + methodName, fakeFeatureFlagsImpl.getClass().getName()),  e);  } catch (ReflectiveOperationException e) {  throw new FlagSetException(fullFlagName, e); 
diff --git a/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/CustomFeatureFlags.java b/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/CustomFeatureFlags.java index 38b4b80..17aaec8 100644 --- a/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/CustomFeatureFlags.java +++ b/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/CustomFeatureFlags.java 
@@ -17,8 +17,10 @@  package android.platform.test.flag.junit;    import java.util.Arrays; +import java.util.HashMap;  import java.util.HashSet;  import java.util.List; +import java.util.Map;  import java.util.Set;  import java.util.function.BiPredicate;  import java.util.function.Predicate; @@ -58,21 +60,30 @@    public List<String> getFlagNames() {  return Arrays.asList( - Flags.FLAG_FLAG_NAME3, - Flags.FLAG_FLAG_NAME4, - Flags.FLAG_RO_ENABLED, - Flags.FLAG_RO_DISABLED - ); + Flags.FLAG_FLAG_NAME3, + Flags.FLAG_FLAG_NAME4, + Flags.FLAG_RO_ENABLED, + Flags.FLAG_RO_DISABLED, + Flags.FLAG_FLAG_FINALIZED);  }    public boolean isFlagReadOnlyOptimized(String flagName) {  return mReadOnlyFlagSet.contains(flagName);  }   - private Set<String> mReadOnlyFlagSet = new HashSet<>( - Arrays.asList( - Flags.FLAG_RO_ENABLED, - Flags.FLAG_RO_DISABLED, - "") - ); + private Set<String> mReadOnlyFlagSet = + new HashSet<>(Arrays.asList(Flags.FLAG_RO_ENABLED, Flags.FLAG_RO_DISABLED, "")); + + private Map<String, Integer> mFinalizedFlags = + new HashMap<>( + Map.ofEntries( + Map.entry(Flags.FLAG_FLAG_FINALIZED, 36), + Map.entry("", Integer.MAX_VALUE))); + + public boolean isFlagFinalized(String flagName) { + if (!mFinalizedFlags.containsKey(flagName)) { + return false; + } + return 99 >= mFinalizedFlags.get(flagName); + }  } 
diff --git a/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/DeviceFlagsValueProviderTest.java b/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/DeviceFlagsValueProviderTest.java index 4ac149b..70a11bf 100644 --- a/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/DeviceFlagsValueProviderTest.java +++ b/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/DeviceFlagsValueProviderTest.java 
@@ -57,11 +57,6 @@  }    @Test - public void getBoolean_validFlag() throws Exception { - assertTrue(mFlagsValueProvider.getBoolean("android.platform.test.flag.junit.flag_name_1")); - } - - @Test  public void getBoolean_notBooleanFlag_throwException() {  assertThrows(  FlagReadException.class, 
diff --git a/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/Flags.java b/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/Flags.java index 6c0c886..74f4a52 100644 --- a/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/Flags.java +++ b/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/Flags.java 
@@ -23,6 +23,8 @@  public static final String FLAG_FLAG_NAME4 = "android.platform.test.flag.junit.flag_name4";  public static final String FLAG_RO_ENABLED = "android.platform.test.flag.junit.ro_enabled";  public static final String FLAG_RO_DISABLED = "android.platform.test.flag.junit.ro_disabled"; + public static final String FLAG_FLAG_FINALIZED = + "android.platform.test.flag.junit.flag_finalized";    /** Returns the flag value. */  public static boolean flagName3() { 
diff --git a/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/SetFlagsRuleTest.java b/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/SetFlagsRuleTest.java index 8c71179..fe0a323 100644 --- a/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/SetFlagsRuleTest.java +++ b/libraries/flag-helpers/junit/test/src/android/platform/test/flag/junit/SetFlagsRuleTest.java 
@@ -43,9 +43,11 @@    public SetFlagsRuleTest(boolean isInitWithDefault) {  this.mIsInitWithDefault = isInitWithDefault; - mSetFlagsRule = new SetFlagsRule(isInitWithDefault - ? SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT - : SetFlagsRule.DefaultInitValueType.NULL_DEFAULT); + mSetFlagsRule = + new SetFlagsRule( + isInitWithDefault + ? SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT + : SetFlagsRule.DefaultInitValueType.NULL_DEFAULT);  }    @Test @@ -125,4 +127,13 @@  mSetFlagsRule.disableFlags(Flags.FLAG_RO_DISABLED);  });  } + + @Test + public void skipFinalizedFlag() { + assertThrows( + AssumptionViolatedException.class, + () -> { + mSetFlagsRule.disableFlags(Flags.FLAG_FLAG_FINALIZED); + }); + }  }